package compiler.mine;

import compiler.Dfa;
import compiler.DfaGroup;
import compiler.DfaGroupManager;

import java.util.Iterator;
import java.util.List;

/**
 * Created by sunzhengjie on 2017/2/6.
 */
public class MinDfa {

    private DfaGroupManager dfaGroupManager;
    private List<Dfa> dfas;
    private int[][] dfaTable;
    private int[][] minimumDfaTable;
    private DfaGroup newGroup;
    private boolean needSegment = false;

    public MinDfa(DfaGroupManager dfaGroupManager, List<Dfa> dfas, int[][] dfaTable) {
        this.dfaGroupManager = dfaGroupManager;
        this.dfas = dfas;
        this.dfaTable = dfaTable;
    }

    public void minimize() {
        initAcceptingDfaGroup();
        initNoAcceptingDfaGroup();
        do {
            needSegment = false;
            segment();
        } while (needSegment);
        createMinimumDfa();
    }


    private void createMinimumDfa() {
        initMinimumDfaTable();
        Iterator<Dfa> iterator = dfas.iterator();
        while (iterator.hasNext()) {
            Dfa first = iterator.next();
            int from = first.stateNum;
            DfaGroup fromGroup = dfaGroupManager.getContainingGroup(from);
            for (int c = 0; c < Lexer.ASCII_COUNT; c++) {
                int to = dfaTable[first.stateNum][c];
                DfaGroup toGroup = dfaGroupManager.getContainingGroup(to);
                if (toGroup != null) {
                    minimumDfaTable[fromGroup.groupNum()][c] = toGroup.groupNum();
                }
            }
        }
    }

    private void initMinimumDfaTable() {
        minimumDfaTable = new int[dfaGroupManager.size()][Lexer.ASCII_COUNT];
        for (int i = 0; i < dfaGroupManager.size(); i++) {
            for (int j = 0; j < Lexer.ASCII_COUNT; j++) {
                minimumDfaTable[i][j] = -1;
            }
        }
    }

    private void segment() {
        for (int i = 0; i < dfaGroupManager.size(); i++) {
            DfaGroup dfaGroup = dfaGroupManager.get(i);
            Dfa first = dfaGroup.get(0);
            int nextIndex = 1;
            Dfa next = dfaGroup.get(nextIndex);
            while (next != null) {
                for (int c = 0; c < Lexer.ASCII_COUNT; c++) {
                    if (doSegment(dfaGroup, first, next, c)) {
                        needSegment = true;
                        break;
                    }
                }
                nextIndex++;
                next = dfaGroup.get(nextIndex);
            }
            dfaGroup.commitRemove();
        }
    }

    private boolean doSegment(DfaGroup dfaGroup, Dfa first, Dfa next, int c) {
        DfaGroup firstGroup = dfaGroupManager.getContainingGroup(dfaTable[first.stateNum][c]);
        DfaGroup nextGroup = dfaGroupManager.getContainingGroup(dfaTable[next.stateNum][c]);
        if (firstGroup != nextGroup) {
            newGroup = dfaGroupManager.createNewGroup();
            newGroup.add(next);
            dfaGroup.tobeRemove(next);
            System.out.println("remove Dfa:" + next.stateNum + " from group:" + firstGroup.groupNum()
                    + " and add it to group:" + newGroup.groupNum());
            return true;
        }
        return false;
    }

    private void initAcceptingDfaGroup() {
        DfaGroup newGroup = dfaGroupManager.createNewGroup();
        for (Dfa dfa : dfas) {
            if (dfa.accepted) {
                newGroup.add(dfa);
            }
        }
        newGroup.printGroup();
    }

    private void initNoAcceptingDfaGroup() {
        DfaGroup newGroup = dfaGroupManager.createNewGroup();
        for (Dfa dfa : dfas) {
            if (!dfa.accepted) {
                newGroup.add(dfa);
            }
        }
        newGroup.printGroup();
    }

    public int[][] getMinimumDfaTable() {
        return minimumDfaTable;
    }

    public static void printMinimumDfaTable(int[][] dfaStateTransformTable) {
        for (int i = 0; i < dfaStateTransformTable.length; i++) {
            System.out.print("area status : " + i);
            for (int j = 0; j < dfaStateTransformTable[i].length; j++) {
                String a = dfaStateTransformTable[i][j] != -1 ? dfaStateTransformTable[i][j] + "" : " ";
                System.out.print(" " + a);
            }
            System.out.println();
        }
    }
}
